using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace WPCoco
{
    public class VertexPositionColorTextureGrid : IGraphicState
    {
        public VertexPositionColorTextureGrid(Size gridSize, Size textureSize)
        {
            this.gridSize = gridSize; 
            this.textureSize = textureSize;
            this.usedTextureSize = textureSize;
            UpdateCellSize();
            CreateBasicEffect();
            BuildGridData();
            DefaultGridState();
        }

        public VertexPositionColorTextureGrid(Size gridSize, Texture2D texture)
            : this(gridSize, new Size(texture.Width, texture.Height))
        {

        }

        public VertexPositionColorTextureGrid(float density, Texture2D texture)
            : this(new Size((int)(texture.Width * density), (int)(texture.Height * density)), texture)
        {

        }

        Size gridSize;

        public bool SetGridSize(Size size)
        {
            if (gridSize == size) return false;
            gridSize = size;
            UpdateCellSize();
            BuildGridData();
            DefaultGridState();
            return true;
        }

        public int GridWidth { get { return gridSize.Width; } }

        public int GridHeight { get { return gridSize.Height; } }

        void BuildGridData()
        {
            vertexs = new VertexPositionColorTexture[(gridSize.Width + 1) * (gridSize.Height + 1)];
            indexes = new Int16[gridSize.Width * gridSize.Height * 6];
            int k = 0;
            for (int i = 0; i < gridSize.Height; i++)
            {
                for (int j = 0; j < gridSize.Width; j++, k += 6)
                {
                    indexes[k + 0] = (Int16)((i) * (gridSize.Width + 1) + (j));
                    indexes[k + 1] = (Int16)((i) * (gridSize.Width + 1) + (j + 1));
                    indexes[k + 2] = (Int16)((i + 1) * (gridSize.Width + 1) + (j));

                    indexes[k + 3] = (Int16)((i) * (gridSize.Width + 1) + (j + 1));
                    indexes[k + 4] = (Int16)((i + 1) * (gridSize.Width + 1) + (j + 1));
                    indexes[k + 5] = (Int16)((i + 1) * (gridSize.Width + 1) + (j));
                }
            }
        }

        Vector2 cellSize = Vector2.Zero;

        void UpdateCellSize()
        {
            cellSize = new Vector2((float)usedTextureSize.Width / (float)gridSize.Width,
                                   (float)usedTextureSize.Height / (float)gridSize.Height);
        }

        Size textureSize = new Size(1, 1);

        public bool SetTextureSize(Size size) 
        {
            if (textureSize == size) return false;
            textureSize = size;
            return true;
        }

        Size usedTextureSize = new Size(1, 1);

        public bool SetUsedTextureSize(Size size)
        {
            if (usedTextureSize == size) return false;
            usedTextureSize = size;
            UpdateCellSize();
            return true;
        }

        VertexPositionColorTexture[] vertexs = null;
        Int16[] indexes = null;

        public VertexPositionColorTexture[] GetVertexsArray() 
        {
            return vertexs;
        }

        int GetIndexForPoint(Point p) 
        {
            return GetIndexForPosition(p.X, p.Y);
        }

        int GetIndexForPosition(int x, int y) 
        {
            return y * (gridSize.Width + 1) + x;
        }

        public void SetColorForVertex(Point p, Color color) 
        {
            int index = GetIndexForPoint(p);
            vertexs[index].Color = color;
        }

        public void SetPositionForVertex(Point p, Vector2 position) 
        {
            int index = GetIndexForPoint(p);
            vertexs[index].Position = new Vector3(position, 0);
        }

        public void SetTextureCoordinateForVertex(Point p, Vector2 uv)
        {
            int index = GetIndexForPoint(p);
            vertexs[index].TextureCoordinate = uv;
        }

        #region Custom States Stuff

        public void DefaultGridState()
        {
            VertexPositionColorTexture v;
            for (int i = 0; i <= gridSize.Height; i++)
            {
                for (int j = 0; j <= gridSize.Width; j++)
                {
                    v = new VertexPositionColorTexture();
                    v.Color = Color.White;
                    v.Position = new Vector3(new Vector2(j, i) * cellSize, 0);
                    v.TextureCoordinate = new Vector2(v.Position.X / (float)textureSize.Width,
                                                      v.Position.Y / (float)textureSize.Height);
                    vertexs[i * (gridSize.Width + 1) + j] = v;
                }
            }
        }

        Vector2 GetDefaultPosition(int x, int y)
        {
            return new Vector2(x, y) * cellSize;
        }

        public void LensGridState(Vector2 center, float radius, float factor)
        {
            Vector2 tsize = new Vector2(textureSize.Width, textureSize.Height);
            float r2 = radius * radius;
            for (int i = 0; i <= gridSize.Height; i++)
            {
                for (int j = 0; j <= gridSize.Width; j++)
                {
                    int index = GetIndexForPosition(j, i);
                    Vector2 pos = GetDefaultPosition(j, i);
                    Vector2 dif = pos - center;
                    float l = dif.LengthSquared();
                    if (l < r2)
                    {
                        float k = (r2 - l) / r2;
                        dif = factor * dif * k;
                        pos = pos - dif;
                    }
                    vertexs[index].TextureCoordinate = pos / tsize;
                }
            }
        }

        public void SwirlGridState(Vector2 center, float radius, float angle)
        {
            angle = MathHelper.ToRadians(angle);
            Vector2 tsize = new Vector2(textureSize.Width, textureSize.Height);
            for (int i = 0; i <= gridSize.Height; i++)
            {
                for (int j = 0; j <= gridSize.Width; j++)
                {
                    int index = GetIndexForPosition(j, i);
                    Vector2 pos = GetDefaultPosition(j, i);
                    Vector2 dif = pos - center;
                    float l = dif.Length();
                    if (l < radius)
                    {
                        float a = (float)Math.Atan2(dif.Y, dif.X);
                        float k = (radius - l) / radius;
                        a += angle * k;
                        pos = center + new Vector2((float)(l * Math.Cos(a)), 
                                                   (float)(l * Math.Sin(a)));
                    }
                    vertexs[index].TextureCoordinate = pos / tsize;
                }
            }
        }

        public void SingleWaveGridState(Vector2 center, float radius, float factor, float amplitude, float phase)
        {
            Vector2 tsize = new Vector2(textureSize.Width, textureSize.Height);
            float r2 = radius * radius;
            for (int i = 0; i <= gridSize.Height; i++)
            {
                for (int j = 0; j <= gridSize.Width; j++)
                {
                    int index = GetIndexForPosition(j, i);
                    Vector2 pos = GetDefaultPosition(j, i);
                    Vector2 dif = pos - center;
                    float l = dif.LengthSquared();
                    if (l < r2)
                    {
                        float k = (r2 - l) / r2;
                        dif = dif * k * (factor * (float)Math.Cos(2 * Math.PI * (Math.Sqrt(l) - phase) / amplitude));
                        pos = pos - dif;
                    }
                    vertexs[index].TextureCoordinate = pos / tsize;
                }
            }
        }

        public class Wave 
        {
            public Vector2 center;
            public float radius;
            public float factor;
            public float amplitude;
            public float phase;
        }

        public void WavesGridState(IList<Wave> waves)
        {
            float Rad = 30;

            //for (int k = waves.Count - 1; k >= 0; k--)
            //    if (waves[k].phase > waves[k].radius + Rad) waves.RemoveAt(k);

            Vector2 tsize = new Vector2(textureSize.Width, textureSize.Height);
            
            
            
            for (int i = 0; i <= gridSize.Height; i++)
            {
                for (int j = 0; j <= gridSize.Width; j++)
                {
                    int index = GetIndexForPosition(j, i);
                    Vector2 pos = GetDefaultPosition(j, i);
                    Vector2 dif = Vector2.Zero;
                    for (int k = 0; k < waves.Count; k++)
                    {
                        Wave w = waves[k];
                        if (pos.X < w.center.X - w.phase - Rad || pos.X > w.center.X + w.phase + Rad ||
                            pos.Y < w.center.Y - w.phase - Rad || pos.Y > w.center.Y + w.phase + Rad) continue;
                        
                        float r = w.phase;
                        float minr = r - Rad;
                        float maxr = r + Rad;

                        float l = (pos - w.center).Length();
                        if (l > minr && l < maxr)
                        {
                            float p = 1;
                            if (l < r) 
                                p = (r - l) / Rad;
                            else 
                                p = (l - r) / Rad;

                            float f = 1 - l / (maxr);
                            dif = dif + (
                               (pos - w.center) * p * f * (
                                  (float)Math.Cos(Math.PI * ((l - r) + Rad) / (2 * Rad))));
                        }
                    }
                    //vertexs[index].Color = new Color(new Vector3(1 - (dif).Length() / 10.0f));
                    pos = pos - dif;
                    vertexs[index].TextureCoordinate = pos / tsize;
                     
                }
            }
        }

        #endregion

        #region Draw

        #region BasicEffect

        BasicEffect basicEffect;

        void CreateBasicEffect()
        {
            rasterizer = new RasterizerState();
            rasterizer.CullMode = CullMode.None;
            rasterizer.MultiSampleAntiAlias = false;

            blend = BlendState.AlphaBlend;

            depthStencil = DepthStencilState.Default;

            sampler = SamplerState.LinearClamp;

            basicEffect = new BasicEffect(Screen.CurrentGraphicsDevice);
            basicEffect.VertexColorEnabled = true;
            basicEffect.TextureEnabled = true;
            basicEffect.Projection = Matrix.CreateOrthographicOffCenter(1, Screen.Width,     // left, right
                                                                        Screen.Height, 1,    // bottom, top
                                                                       -1, 1);
            basicEffect.World = Matrix.Identity;
        }

        #endregion

        public void Draw(Texture2D texture, Matrix world) 
        {
            RasterizerState rlast = Screen.CurrentGraphicsDevice.RasterizerState;
            Screen.CurrentGraphicsDevice.RasterizerState = rasterizer;
            BlendState blast = Screen.CurrentGraphicsDevice.BlendState;
            Screen.CurrentGraphicsDevice.BlendState = blend;
            DepthStencilState dlast = Screen.CurrentGraphicsDevice.DepthStencilState;
            Screen.CurrentGraphicsDevice.DepthStencilState = depthStencil;

            basicEffect.Texture = texture;
            basicEffect.World = world;

            SamplerState slast = Screen.CurrentGraphicsDevice.SamplerStates[0];
            Screen.CurrentGraphicsDevice.SamplerStates[0] = sampler;

            for (int i = 0; i < basicEffect.CurrentTechnique.Passes.Count; i++)
            {
                EffectPass pass = basicEffect.CurrentTechnique.Passes[i];

                pass.Apply();
                Screen.CurrentGraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColorTexture>
                       (PrimitiveType.TriangleList,
                       vertexs,
                       0,
                       ((1 + gridSize.Width) * (1 + gridSize.Height)),
                       indexes,
                       0,
                       2 * (gridSize.Width * gridSize.Height));
            }

            Screen.CurrentGraphicsDevice.RasterizerState = rlast;
            Screen.CurrentGraphicsDevice.BlendState = blast;
            Screen.CurrentGraphicsDevice.DepthStencilState = dlast;
            Screen.CurrentGraphicsDevice.SamplerStates[0] = slast;
        }

        #endregion

        #region IGraphicState Members

        SamplerState sampler = SamplerState.LinearClamp;

        public SamplerState Sampler
        {
            get { return sampler; }
            set { sampler = value; }
        }

        DepthStencilState depthStencil = DepthStencilState.Default;

        public DepthStencilState DepthStencil
        {
            get { return depthStencil; }
            set { depthStencil = value; }
        }

        BlendState blend = BlendState.AlphaBlend;

        public BlendState Blend
        {
            get { return blend; }
            set { blend = value; }
        }

        RasterizerState rasterizer = RasterizerState.CullNone;

        public RasterizerState Rasterizer
        {
            get { return rasterizer; }
            set { rasterizer = value; }
        }

        #endregion
    }
}
